package work.linruchang.util;


import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;

import javax.persistence.Column;
import java.beans.Transient;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Stream;

/**
 * 增强的反射处理工具
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2021/08/27
 * @since 1.8
 **/
public class EnhanceReflectUtil extends ReflectUtil {


    public static Method[] getMethods(Func1 func1) throws SecurityException {
        return getMethods(func1.getClass());
    }

    /**
     * 获取方法 == 不忽略大小写
     *
     * @param clazz      目标类
     * @param methodName 方法名
     * @return
     * @throws SecurityException
     */
    public static Method[] getMethodsByName(Class<?> clazz, String methodName) throws SecurityException {
        return getMethodsByName(clazz, methodName, null);
    }

    /**
     * 获取方法 == 不忽略大小写
     *
     * @param clazz      目标类
     * @param methodName 方法名
     * @param sumParam   方法入参个数
     * @return
     * @throws SecurityException
     */
    public static Method[] getMethodsByName(Class<?> clazz, String methodName, Integer sumParam) throws SecurityException {
        return getMethodsByName(clazz, false, methodName, sumParam);
    }

    /**
     * 获取方法
     *
     * @param clazz      目标类
     * @param ignoreCase 方法名是否忽略大小写
     * @param methodName 方法名
     * @param sumParam   方法入参个数
     * @return
     * @throws SecurityException
     */
    public static Method[] getMethodsByName(Class<?> clazz, boolean ignoreCase, String methodName, Integer sumParam) throws SecurityException {
        if (null == clazz || StrUtil.isBlank(methodName)) {
            return new Method[0];
        }
        List<Method> methodList = new ArrayList<>();
        final Method[] methods = getMethods(clazz);
        if (ArrayUtil.isNotEmpty(methods)) {
            for (Method method : methods) {
                method.setAccessible(true);
                if (StrUtil.equals(methodName, method.getName(), ignoreCase)) {
                    if (sumParam != null) {
                        if (method.getParameterTypes().length == sumParam) {
                            methodList.add(method);
                        }
                    } else {
                        methodList.add(method);
                    }
                }
            }
        }

        return ArrayUtil.toArray(methodList, Method.class);
    }

    /**
     * 判断此字段类型是否是基本类型
     *
     * @param clazz     目标对象的反射
     * @param fieldName 字段名
     * @return
     */
    public static boolean isFieldPrimitiveType(Class clazz, String fieldName) {
        boolean result = false;
        if (ObjectUtil.isAllNotEmpty(clazz, fieldName)) {
            result = isFieldPrimitiveType(getField(clazz, fieldName));
        }
        return result;
    }

    /**
     * 判断此字段类型是否是基本类型
     *
     * @param field
     * @return
     */
    public static boolean isFieldPrimitiveType(Field field) {
        boolean result = field != null ? field.getType().isPrimitive() : false;
        return result;
    }

    /**
     * 将所有数据库非基本类型的字段置空
     *
     * @param bean
     * @param <T>
     * @return
     */
    public static <T> T setAllFieldValueEmpty(T bean) {
        if (bean != null) {
            Class<?> clazz = bean.getClass();
            String[] dbFieldNames = getDBFieldNames(clazz);
            if (ArrayUtil.isNotEmpty(dbFieldNames)) {
                for (String dbFieldName : dbFieldNames) {
                    if (!isFieldPrimitiveType(clazz, dbFieldName)) {
                        setFieldValue(bean, dbFieldName, null);
                    }
                }
            }
        }
        return bean;
    }

    public static Map<Field, Annotation> getFieldByAnnotation(Class targetClazz, final Class fieldAnnotation) {
        final Map<Field, Annotation> result = new HashMap<>();
        if (ObjectUtil.isAllNotEmpty(targetClazz, fieldAnnotation)) {
            Field[] fields = getFields(targetClazz);
            ArrayUtil.filter(fields, new Filter<Field>() {
                @Override
                public boolean accept(Field field) {
                    Annotation[] fieldAnnotations = field.getDeclaredAnnotations();
                    Annotation findAnnotation = CollUtil.findOne(Arrays.asList(fieldAnnotations), new Filter<Annotation>() {
                        @Override
                        public boolean accept(Annotation annotation) {
                            return ObjectUtil.equal(annotation.annotationType(), fieldAnnotation);
                        }
                    });
                    if (findAnnotation != null) {
                        result.put(field, findAnnotation);
                    }
                    return true;
                }
            });
        }
        return CollUtil.isEmpty(result) ? Collections.<Field, Annotation>emptyMap() : result;
    }

    /**
     * 获取某个Bean【targetClazz】中的某个字段上的注解【annotationClazz】里面的注解值为【annotationValue】 的 字段
     *
     * @param targetClazz     目标Bean
     * @param annotationClazz 字段注解
     * @param annotationValue 注解值
     * @return
     */
    public static Field getFieldByAnnotationAndValue(final Class targetClazz, final Class annotationClazz, final Object annotationValue) {
        Field field = null;
        if (ObjectUtil.isAllNotEmpty(targetClazz, annotationClazz)) {
            Field[] fields = getFields(targetClazz);
            if (ArrayUtil.isNotEmpty(fields)) {
                field = CollUtil.findOne(Arrays.asList(fields), new Filter<Field>() {
                    @Override
                    public boolean accept(Field field) {
                        field.setAccessible(true);
                        Object fieldAnnotationValue = AnnotationUtil.getAnnotationValue(field, annotationClazz, "value");
                        return ObjectUtil.equal(fieldAnnotationValue, annotationValue);
                    }
                });
            }
        }
        return field;
    }

    /**
     * 比较targetObject、targetObject2中的对应fieldNames字段值是否都相同
     *
     * @param targetObject  目标对象1
     * @param targetObject2 目标对象2
     * @param fieldNames    需要比对的字段名
     * @return
     */
    public static boolean fieldsAllEqualFlag(final Object targetObject, final Object targetObject2, Collection<String> fieldNames) {
        if (ObjectUtil.isAllEmpty(targetObject, targetObject2)) {
            return true;
        } else {
            if (ObjectUtil.hasEmpty(targetObject, targetObject2)) {
                return false;
            } else {
                String notEqualFieldName = CollUtil.findOne(fieldNames, new Filter<String>() {
                    @Override
                    public boolean accept(String fieldName) {
                        Object fieldValue = getFieldValue(targetObject, fieldName);
                        Object fieldValue2 = getFieldValue(targetObject2, fieldName);
                        boolean equal = ObjectUtil.equal(fieldValue, fieldValue2);
                        return !equal;
                    }
                });
                return StrUtil.isBlank(notEqualFieldName);
            }
        }
    }

    /**
     * 比较targetObject、targetObject2中的对应fieldNames字段值是否都相同
     *
     * @param targetObject  目标对象1
     * @param targetObject2 目标对象2
     * @param fieldNames    需要比对的字段名
     * @return
     */
    public static boolean fieldsAllEqualFlag(final Object targetObject, final Object targetObject2, String... fieldNames) {
        if (fieldNames == null) {
            fieldNames = new String[0];
        }
        return fieldsAllEqualFlag(targetObject, targetObject2, Arrays.asList(fieldNames));
    }

    /**
     * 比较targetObject、targetObject2中的对应fieldNames字段值是否含有不相同的
     *
     * @param targetObject  目标对象1
     * @param targetObject2 目标对象2
     * @param fieldNames    需要比对的字段名
     * @return 存在字段值不相同则返回true
     */
    public static boolean hasFieldsNotEqualFlag(final Object targetObject, final Object targetObject2, String... fieldNames) {
        return !fieldsAllEqualFlag(targetObject, targetObject2, fieldNames);
    }

    /**
     * 比较targetObject、targetObject2中的对应fieldNames字段值是否含有不相同的
     *
     * @param targetObject  目标对象1
     * @param targetObject2 目标对象2
     * @param fieldNames    需要比对的字段名
     * @return 存在字段值不相同则返回true
     */
    public static boolean hasFieldsNotEqualFlag(final Object targetObject, final Object targetObject2, Collection<String> fieldNames) {
        return !fieldsAllEqualFlag(targetObject, targetObject2, fieldNames);
    }

    /**
     * 获取反射targetClazz所有字段【包括静态字段】，排除字段中含有某些注解【excludeFieldAnnotations】的字段
     *
     * @param targetClazz
     * @param excludeFieldAnnotations
     * @return
     */
    public static Field[] getFieldsExcludeHasAnnotationField(Class targetClazz, final Class... excludeFieldAnnotations) {
        Assert.notNull(targetClazz, "目标反射不能为空，请检查");
        Field[] fields = getFields(targetClazz);
        if (ArrayUtil.isNotEmpty(excludeFieldAnnotations)) {
            fields = ArrayUtil.filter(fields, new Filter<Field>() {
                @Override
                public boolean accept(Field field) {
                    Annotation[] fieldAnnotations = field.getDeclaredAnnotations();
                    Annotation excludeAnnotation = CollUtil.findOne(Arrays.asList(fieldAnnotations), new Filter<Annotation>() {
                        @Override
                        public boolean accept(Annotation annotation) {
                            return ArrayUtil.contains(excludeFieldAnnotations, annotation.annotationType());
                        }
                    });
                    return excludeAnnotation == null;
                }
            });
        }
        return fields;
    }

    /**
     * 获取反射targetClazz所有成员字段【排除静态字段】并 排除字段中含有某些注解【excludeFieldAnnotations】的字段
     *
     * @param targetClazz
     * @param excludeFieldAnnotations
     * @return
     */
    public static Field[] getMemberFieldsExcludeHasAnnotationField(Class targetClazz, final Class... excludeFieldAnnotations) {
        Field[] fields = getFieldsExcludeHasAnnotationField(targetClazz, excludeFieldAnnotations);
        return ArrayUtil.filter(fields, new Filter<Field>() {
            @Override
            public boolean accept(Field field) {
                return !Modifier.isStatic(field.getModifiers());
            }
        });
    }

    /**
     * 获取数据库字段Field
     * 排除Transient注解的字段
     *
     * @param targetClazz
     * @return
     */
    public static Field[] getDBFields(Class targetClazz) {
        return getMemberFieldsExcludeHasAnnotationField(targetClazz, Transient.class);
    }

    /**
     * 获取Bean对象里面数据库字段名
     * 排除Transient注解的字段
     *
     * @param targetClazz
     * @param camelToUnderlineFlag 是否需要将字段名驼峰转下划线形式
     * @return
     */
    public static String[] getDBFieldNames(Class targetClazz, boolean camelToUnderlineFlag) {
        return getDBFieldNames(targetClazz, camelToUnderlineFlag, null);
    }

    /**
     * 获取Bean对象里面数据库字段名
     * 排除Transient注解的字段
     *
     * @param targetClazz
     * @param camelToUnderlineFlag 是否需要将字段名驼峰转下划线形式
     * @param excludeFieldNames    需要排除的字段
     * @return
     */
    public static String[] getDBFieldNames(Class targetClazz, boolean camelToUnderlineFlag, List<String> excludeFieldNames) {
        Field[] dbFields = getMemberFieldsExcludeHasAnnotationField(targetClazz, Transient.class);
        ArrayList<String> fieldNameList = new ArrayList<>(dbFields.length);
        for (int i = 0; i < dbFields.length; i++) {
            String fieldName = dbFields[i].getName();
            if (CollUtil.isEmpty(excludeFieldNames)) {
                fieldNameList.add(camelToUnderlineFlag ? EnhanceStrUtil.camelToUnderline(fieldName) : fieldName);
            } else {
                if (!CollUtil.containsAny(excludeFieldNames, Arrays.asList(fieldName, EnhanceStrUtil.camelTransfersBetweenUnderline(fieldName)))) {
                    fieldNameList.add(camelToUnderlineFlag ? EnhanceStrUtil.camelToUnderline(fieldName) : fieldName);
                }
            }
        }
        return fieldNameList.toArray(new String[fieldNameList.size()]);
    }

    /**
     * 获取Bean对象里面数据库字段名 == Bean属性名是啥就是，不会做任何处理
     * 排除Transient注解的字段
     *
     * @param targetClazz
     * @return
     */
    public static String[] getDBFieldNames(Class targetClazz) {
        return getDBFieldNames(targetClazz, false);
    }

    /**
     * 获取Bean对象里面数据库字段名 == Bean属性名是啥就是，不会做任何处理
     * 排除Transient注解的字段
     *
     * @param targetClazz
     * @param excludeFieldNames 需要排除的字段
     * @return
     */
    public static String[] getDBFieldNames(Class targetClazz, List<String> excludeFieldNames) {
        return getDBFieldNames(targetClazz, false, excludeFieldNames);
    }

    /**
     * 获取某个Bean的所有数据库列名
     *
     * @param targetClazz
     * @return
     */
    public static String[] getDBColumnNames(Class targetClazz) {
        return getDBColumnNames(targetClazz, getDBFieldNames(targetClazz));
    }

    /**
     * 获取数据库列名
     * 查看字段是否有@Column注解且里面的@Column注解的name属性不为空，且返回列名为属性name
     * 否则都会将field.getName()的值转成下划线形式
     *
     * @param targetClazz 字段反射
     * @param fieldNames  Bean的字段属性名
     * @return
     */
    public static String[] getDBColumnNames(Class targetClazz, String[] fieldNames) {
        if (ArrayUtil.isEmpty(fieldNames)) {
            return fieldNames;
        } else {
            String[] result = new String[fieldNames.length];
            for (int i = 0; i < fieldNames.length; i++) {
                result[i] = getDBColumnName(targetClazz, fieldNames[i]);
            }
            return result;
        }
    }

    /**
     * 获取数据库列名
     * 查看字段是否有@Column注解且里面的@Column注解的name属性不为空，且返回列名为属性name
     * 否则都会将field.getName()的值转成下划线形式
     *
     * @param targetClazz 字段反射
     * @param fieldName   Bean的字段属性名
     * @return
     */
    public static String getDBColumnName(Class targetClazz, String fieldName) {
        Field field = getField(targetClazz, fieldName);
        return getDBColumnName(field != null ? field : getField(targetClazz, EnhanceStrUtil.camelTransfersBetweenUnderline(fieldName)));
    }

    /**
     * 获取此字段在Bean中的类型
     *
     * @param targetClazz 目标表
     * @param fieldName   字段名
     * @return
     */
    public static Class getDBColumnNameClassType(Class targetClazz, String fieldName) {
        Field field = getField(targetClazz, fieldName);
        field = field != null ? field : getField(targetClazz, EnhanceStrUtil.camelTransfersBetweenUnderline(fieldName));
        return field != null ? field.getType() : null;
    }


    /**
     * 获取数据库列名
     * 查看字段是否有@Column注解且里面的@Column注解的name属性不为空，且返回列名为属性name
     * 否则都会将field.getName()的值转成下划线形式
     *
     * @param field 字段反射
     * @return
     */
    public static String getDBColumnName(Field field) {
        String dBColumnName = null;
        if (field != null) {
            String fileName = field.getName();
            Column annotation = field.getAnnotation(Column.class);
            if (annotation != null) {
                dBColumnName = EnhanceStrUtil.blankToDefault(annotation.name(), EnhanceStrUtil.camelToUnderline(fileName));
            } else {
                dBColumnName = EnhanceStrUtil.camelToUnderline(fileName);
            }
        }
        return dBColumnName;
    }

    /**
     * 设置字段值  找不到的字段则不进行设置
     *
     * @param targetBean 目标Bean
     * @param fieldName  目标Bean里面的字段
     * @param fieldValue 需要设置成的值
     * @return
     */
    public static <T> T setFieldValue2(T targetBean, String fieldName, Object fieldValue) {
        if (targetBean == null) {
            return targetBean;
        }
        Field field = getField(targetBean.getClass(), fieldName);
        if (field != null) {
            setFieldValue(targetBean, field, fieldValue);
        }
        return targetBean;
    }

    /**
     * 设置字段值 【找不到则直接忽略字段设置】
     * 如果某字段无值则将设置为默认值deafultValue
     *
     * @param targetBean   目标Bean对象
     * @param fieldName    字段名
     * @param deafultValue 默认值
     * @return 返回形参targetBean
     */
    public static <T> T setEmptyToDefaultFieldValue(T targetBean, String fieldName, Object deafultValue) {
        try {
            if (ObjectUtil.isAllNotEmpty(targetBean, fieldName, deafultValue)) {
                Field field = getField(targetBean.getClass(), fieldName);
                if (field != null) {
                    field.setAccessible(true);
                    if (ObjectUtil.isEmpty(field.get(targetBean))) {
                        field.set(targetBean, deafultValue);
                    }
                }
            }
        } catch (Exception e) {

        } finally {
            return targetBean;
        }
    }


    /**
     * 从包package中获取所有的Class == 包括子包
     *
     * @param pack
     * @return
     */
    public static Set<Class<?>> getClasses(String pack) {
        Assert.notBlank(pack, "缺乏包名，请检查");
        Set<Class<?>> classes = ReflectClassUtils.getClasses(pack);
        return classes;
    }

    public static Set<Class<?>> getDbClasses(String pack) {
        Set<Class<?>> classes = getClasses(pack);
        Set<Class<?>> result = new LinkedHashSet<>();
        for (Class clazz : classes) {
            Annotation[] annotations = clazz.getAnnotations();
            Annotation dbAnnotation = CollUtil.findOne(Arrays.asList(annotations), new Filter<Annotation>() {
                @Override
                public boolean accept(Annotation annotation) {
                    return StrUtil.containsAnyIgnoreCase(annotation.toString(), "Table");
                }
            });
            if (dbAnnotation != null) {
                result.add(clazz);
            }
        }
        return result;
    }

    /**
     * 获取所有字段以及字段值的映射
     *
     * @param obj Bean非Class实例
     * @return 字段映射
     * @since 4.1.17
     */
    public static Map<Field, Object> getFieldToValueMap(Object obj) {
        if (null != obj && !(obj instanceof Class)) {
            final Field[] fields = getFields(obj.getClass());
            if (ArrayUtil.isNotEmpty(fields)) {
                final Map<Field, Object> result = new HashMap();
                Stream.of(fields).forEach(field -> {
                    result.put(field, ReflectUtil.getFieldValue(obj, field));
                });
                return result;
            }
        }
        return MapUtil.empty();
    }

    /**
     * 获取所有字段名以及字段值的映射
     *
     * @param obj Bean非Class实例
     * @return 字段映射
     * @since 4.1.17
     */
    public static Map<String, Object> getFieldNameToValueMap(Object obj) {
        Map<Field, Object> fieldToValueMap = getFieldToValueMap(obj);
        if(CollUtil.isNotEmpty(fieldToValueMap)) {
            final Map<String,Object> resultMap = new HashMap();
            fieldToValueMap.forEach((field, fieldValue) -> {
                resultMap.put(getFieldName(field),fieldValue);
            });
            return resultMap;
        }
        return MapUtil.empty();
    }

    /**
     * 获取字段的值
     *
     * @param obj bean对象，如果是static字段，此处为类class
     * @param withSuperClassFields 是否包括父类的字段列表
     * @param ignoreFieldNames 忽略的字段
     * @return 字段值数组
     * @since 4.1.17
     */
    public static Object[] getFieldsValue(Object obj, boolean withSuperClassFields, String... ignoreFieldNames) {
        if(withSuperClassFields) {
            return getFieldMap(obj.getClass()).entrySet().stream()
                    .filter(mapEntry -> {
                        String fieldName = mapEntry.getKey();
                        return !ArrayUtil.contains(ignoreFieldNames, fieldName);
                    })
                    .map(Map.Entry::getValue)
                    .map(field -> ReflectUtil.getFieldValue(obj, field))
                    .toArray();
            // return getFieldsValue(obj);
        }else {
            if(obj != null) {
                Field[] fieldsDirectlyFields = getFieldsDirectly(obj.getClass(), false);

                fieldsDirectlyFields = Stream.of(fieldsDirectlyFields).filter(field -> {
                    return !ArrayUtil.contains(ignoreFieldNames, field.getName());
                }).toArray(Field[]::new);

                Object[] result = ArrayUtil.newArray(ArrayUtil.length(fieldsDirectlyFields));
                for(int i= 0; i<fieldsDirectlyFields.length; i++ ) {
                    result[i] = ReflectUtil.getFieldValue(obj,fieldsDirectlyFields[i]);
                }
                return result;
            }
        }
        return null;
    }

    /**
     * 获取字段的值
     *
     * @param obj bean对象，如果是static字段，此处为类class
     * @param withSuperClassFields 是否包括父类的字段列表
     * @param ignoreFieldNames 忽略的字段
     * @return 字段值数组
     * @since 4.1.17
     */
    public static <T> Object[] getFieldsValue2(Object obj, boolean withSuperClassFields, Func1<T,?>... ignoreFieldNames) {
        ignoreFieldNames = ignoreFieldNames != null ? ignoreFieldNames : new Func1[0];
        String[] ignoreFieldNameStrs = Stream.of(ignoreFieldNames).map(LambdaUtil::getFieldName)
                .toArray(String[]::new);
        return getFieldsValue(obj,withSuperClassFields,ignoreFieldNameStrs);
    }

}
